﻿using System;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

using Cronus.Windows;
using Cronus.ViewModel;

namespace Cronus.View
{
    /// <summary>
    /// Delegate used when a task starts being timed.
    /// </summary>
    /// <param name="strTaskName">Name of the task that is starting to be timed.</param>
    public delegate void UpdateTimedTask(string strTaskName);

    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        /// <summary>
        /// This is a private container class.  It is only used to store
        /// which task is currently being timed so that we are able
        /// to "shut it off" after we're done timing it.
        /// </summary>
        private class CurrentTask
        {
            //*** Constants ***//
            //*** Static ***//
            //*** Member Variables ***//
            private Button _CurrentTask;
            private Brush _OriginalBackgroundBrush;
            private Brush _OriginalForegroundBrush;

            //*** Properties ***//
            /// <summary>
            /// Returns the name of the task this object represents.
            /// </summary>
            public string TaskName
            {
                get
                {
                    return (((TextBlock)_CurrentTask.Content).Text);
                }
            }

            //*** Constructors ***//
            public CurrentTask(Button aTask)
            {
                _CurrentTask = aTask;
                _OriginalBackgroundBrush = aTask.Background;
                _OriginalForegroundBrush = aTask.Foreground;
            }

            //*** Methods ***//
            /// <summary>
            /// This function will reset the original Foreground
            /// and Background colors on the associated button.
            /// </summary>
            public void ResetOriginalColors()
            {
                _CurrentTask.Foreground = _OriginalForegroundBrush;
                _CurrentTask.Background = _OriginalBackgroundBrush;
            }
        }

        //*** Constants ***//
        private string STR_FILE_PATH = @"CronusData.xml";
        private long TIME_TO_SAVE_MS = 1000 * 60 * 5; // 5 minutes

        private int MAX_RESIZE_ROWS = 10;
        private int TASKS_PER_ROW = 4;
        private int HEIGHT_PER_ROW = 50;
        private int DEFAULT_ROW_HEIGHT = 215;

        //*** Static ***//
        //*** Member Variables ***//
        private DataModel _DataModel;
        private CurrentTask _CurrentTask = null;

        private System.Windows.Forms.NotifyIcon _TrayIcon;

        //*** Properties ***//
        //*** Constructors ***//
        public MainWindow()
        {
            // Initialize WPF components.
            InitializeComponent();
            Title = Cronus.View.Properties.Resources.Title;

            // NEW_DS
            // bms_todo: Move configuration information to the configuration file.
            var config = new DataModelConfiguration()
            {
                FullFilePath = STR_FILE_PATH,
                SaveTimeMilliseconds = TIME_TO_SAVE_MS
            };
            _DataModel = new DataModel(config); // Need to call Dispose() on this later.
            _DataModel.InitializeDataModel();

            // Create the tray icon
            _TrayIcon = new System.Windows.Forms.NotifyIcon();
            _TrayIcon.Icon = Cronus.View.Properties.Resources.FireCreature;
            _TrayIcon.Visible = true;
            _TrayIcon.DoubleClick += new EventHandler(TrayIcon_DoubleClick);
            _TrayIcon.Text = Cronus.View.Properties.Resources.Title;
            StateChanged += new EventHandler(MainWindow_StateChanged);

            // Setup the stop button
            _StopButton.Background = new LinearGradientBrush(Colors.LightGray, Colors.Red, 90.0);
            _StopButton.IsEnabled = false;
        }

        //*** Methods ***//
        /// <summary>
        /// As the window is closed, this function is called.  This function
        /// will perform any clean up work.
        ///     [1]: Stop timing, no support for knowing if timing on startup.
        ///     [2]: One last save of the dataset.
        ///     [3]: Clean up the system tray icon.
        /// </summary>
        /// <param name="sender">Not used.</param>
        /// <param name="e">Not used.</param>
        private void Window_Closed(object sender, EventArgs e)
        {
            // [1]: Stop timing, we don't have any support for updating the
            //      UI on startup to show that we're timing.
            _DataModel.StopTiming();

            // [2]: Dispose also performs a final save on the dataset.
            _DataModel.Dispose();

            // [3]: Clean up the system try icon.
            _TrayIcon.Visible = false;
            _TrayIcon.Dispose();
        }

        /// <summary>
        /// The function updates the system tray icon with the current task being timed.
        /// </summary>
        /// <param name="strTaskName">Name of the task being timed, empty if no task.</param>
        private void OnTaskBeingTimed(string strTaskName)
        {
            // If no task is being updated, use the default text.
            if ((null == strTaskName) || (String.Empty == strTaskName))
            {
                _TrayIcon.Text = Cronus.View.Properties.Resources.Title;
            }
            else
            {
                // Put the name of the task being timed (i.e. "Cronus (task1)").
                _TrayIcon.Text = String.Format( "{0} ({1})",
                                                Cronus.View.Properties.Resources.Title,
                                                strTaskName);
            }
        }

        /// <summary>
        /// This is a callback when the state of the window changes (i.e.
        /// maximized, minimized and normal sized).
        /// </summary>
        /// <param name="sender">Not used.</param>
        /// <param name="e">Not used.</param>
        void MainWindow_StateChanged(object sender, EventArgs e)
        {
            // When the user minimizes the window, hide it to the
            // system tray.
            if (WindowState == WindowState.Minimized)
            {
                Hide();
            }
        }

        /// <summary>
        /// Callback when the user double-clicks the system tray icon.  This
        /// will bring the Cronus window to the front.
        /// </summary>
        /// <param name="sender">Not used.</param>
        /// <param name="e">Not used.</param>
        void TrayIcon_DoubleClick(object sender, EventArgs e)
        {
            Show();
            WindowState = WindowState.Normal;
            Activate();
        }

        /// <summary>
        /// This function checks for the return key to be pressed in the
        /// TextBox users can enter task titles in.  If it is pressed,
        /// it simulates pressing the "Add Task" button.
        /// </summary>
        /// <param name="sender">Not used.</param>
        /// <param name="e">Not used.</param>
        private void NewTask_KeyDown(object sender, KeyEventArgs e)
        {
            if (Key.Return == e.Key)
            {
                EnterNewTask_Click(null, null);
            }
        }

        /// <summary>
        /// This is a handler when the user enters a new task.
        /// </summary>
        /// <param name="sender">Not used.</param>
        /// <param name="e">Not used.</param>
        private void EnterNewTask_Click(object sender, RoutedEventArgs e)
        {
            if ((null != _NewTask.Text) && (0 < _NewTask.Text.Length))
            {
                var aTaskActivities = new TaskActivities(_DataModel);
                aTaskActivities.GetOrCreateTask(_NewTask.Text);
                _NewTask.Text = String.Empty;
                ResizeRowArea();
            }
        }

        /// <summary>
        /// By default at startup there is only enough room to display
        /// one row of tasks.  This function will expand that area to
        /// fit all the tasks w/o users having to manually resize (up
        /// to a limit of MAX_RESIZE_ROWS).
        /// </summary>
        private void ResizeRowArea()
        {
            // Figure out how many rows there are.
            int nRows = (_DataModel.NumberOfTasks - 1) / TASKS_PER_ROW;
            if (MAX_RESIZE_ROWS < nRows)
            {
                // Don't expand the screen forever, cap it at the max.
                nRows = MAX_RESIZE_ROWS;
            }
            else if (0 > nRows) // Can't have negative row counts.
            {
                nRows = 0;
            }
            Height = DEFAULT_ROW_HEIGHT + (nRows * HEIGHT_PER_ROW);
        }

        /// <summary>
        /// This is a callback when the user wants to see the total time for
        /// tasks.  This will open a new window.
        /// </summary>
        /// <param name="sender">Not used.</param>
        /// <param name="e">Not used.</param>
        private void ShowTotals_Click(object sender, RoutedEventArgs e)
        {
            TaskTotals aDialog = new TaskTotals();
            //aDialog.CronusDataSet = _CronusData;
            aDialog.ShowDialog();
        }

        /// <summary>
        /// This button shows the dialog to allow users to review totals on a
        /// per month basis.
        /// </summary>
        /// <param name="sender">Not used.</param>
        /// <param name="e">Not used.</param>
        private void ShowMonthlyTotals_Click(object sender, RoutedEventArgs e)
        {
            MonthlySummary aDialog = new MonthlySummary();
            //aDialog.CronusDataSet = _CronusData;
            aDialog.ShowDialog();
        }

        /// <summary>
        /// This is a callback when the user clicks the Edit Tasks button.
        /// It will open a new window and allow users to edit their tasks.
        /// </summary>
        /// <param name="sender">Not used.</param>
        /// <param name="e">Not used.</param>
        private void EditTaskBtn_Click(object sender, RoutedEventArgs e)
        {
            EditTask aDialog = new EditTask();
            //aDialog.CronusDataSet = _CronusData;
            aDialog.ShowDialog();
        }

        /// <summary>
        /// This is callback when the user clicks the Edit Events button.
        /// It will open a new window and allow users to edit their events.
        /// </summary>
        /// <param name="sender">Not used.</param>
        /// <param name="e">Not used.</param>
        private void EditEvents_Click(object sender, RoutedEventArgs e)
        {
            // Create the dialog to edit events then open it.  Creating the static
            // property in EditEvents actually causes the program to hang when closing.
            //EditEvents.CronusDataSet = _CronusData;
            EditEvents aDialog = new EditEvents();
            aDialog.ShowDialog();
            EditEvents.CronusDataSet = null;
        }

        /// <summary>
        /// This is a handler when the user clicks on the "Project Setup" button.
        /// </summary>
        /// <param name="sender">Not used.</param>
        /// <param name="e">Not used.</param>
        private void ProjectSetup_Click(object sender, RoutedEventArgs e)
        {
            ProjectSetup aDialog = new ProjectSetup();
            //aDialog.CronusDataSet = _CronusData;
            aDialog.ShowDialog();
        }

        /// <summary>
        /// This callback will stop timing the last task and start
        /// timing the next task.
        /// </summary>
        /// <param name="sender">The sender, used to figure out who starts.</param>
        /// <param name="e">Not used.</param>
        private void TaskStart_Click(object sender, RoutedEventArgs e)
        {
            // Local variables
            Button aTask = sender as Button;
            string strTask = String.Empty;

            if (null != aTask)
            {
                strTask = ((TextBlock)aTask.Content).Text;
                if (String.Empty != strTask)
                {
                    // The user has clicked on the same task twice, ignore
                    // click since nothing has changed.
                    if ((null != _CurrentTask) &&
                        (_CurrentTask.TaskName == strTask))
                    {
                        return;
                    }

                    _DataModel.StopTiming();

                    // Start timing the new task and since we're timing,
                    // update the stop button.
                    _DataModel.StartTiming(strTask);
                    _StopButton.IsEnabled = true;
                    _StopButton.Foreground = Brushes.White;

                    // Set the old task back to default.
                    if (null != _CurrentTask)
                    {
                        _CurrentTask.ResetOriginalColors();
                    }

                    // Save the current state of the new button.
                    _CurrentTask = new CurrentTask(aTask);

                    // Update it to show it in progress.
                    aTask.Background = new LinearGradientBrush(Colors.LightGray, Colors.Green, 90.0);
                    aTask.Foreground = Brushes.White;
                }
            }
        }

        /// <summary>
        /// This function handles the clicking of the stop button.  If
        /// the timer is currently being used, it will be stopped.
        /// </summary>
        /// <param name="sender">Not used.</param>
        /// <param name="e">Not used.</param>
        private void StopButton_Click(object sender, RoutedEventArgs e)
        {
            _DataModel.StopTiming();
            _StopButton.IsEnabled = false;
            _StopButton.Foreground = Brushes.Gray;

            _CurrentTask.ResetOriginalColors();
            _CurrentTask = null;
        }
    }
}
